package controllers;

import play.Play;
import play.mvc.Controller;

import com.caucho.hessian.io.*;
import com.caucho.hessian.server.HessianSkeleton;
import com.caucho.services.server.GenericService;
import com.caucho.services.server.Service;
import com.caucho.services.server.ServiceContext;

import java.io.*;
import java.util.logging.*;

/**
 * Servlet for serving Hessian services.
 */
public class Hessian extends Controller {

	private static Class _homeAPI;
	private static Object _homeImpl;

	private static Class _objectAPI;
	private static Object _objectImpl;

	private static HessianSkeleton _homeSkeleton;
	private static HessianSkeleton _objectSkeleton;

	private static SerializerFactory _serializerFactory;

	public static boolean isDebug;


	/**
	 * Sets the serializer factory.
	 */
	public static void setSerializerFactory(SerializerFactory factory) {
		_serializerFactory = factory;
	}

	/**
	 * Gets the serializer factory.
	 */
	public static SerializerFactory getSerializerFactory() {
		if (_serializerFactory == null)
			_serializerFactory = new SerializerFactory();

		return _serializerFactory;
	}

	/**
	 * Sets the serializer send collection java type.
	 */
	public static void setSendCollectionType(boolean sendType) {
		getSerializerFactory().setSendCollectionType(sendType);
	}

	/**
	 * Initialize the service, including the service object.
	 */
	public static void init() throws Exception {

		try {
			if (_homeImpl != null) {
			} else if (Play.configuration.getProperty("hessian.home-class") != null) {
				String className = Play.configuration
						.getProperty("hessian.home-class"); // getInitParameter("home-class");
				Class homeClass = loadClass(className);
				_homeImpl = homeClass.newInstance();
				init();
			} else if (Play.configuration.getProperty("hessian.service-class") != null) {
				String className = Play.configuration
						.getProperty("hessian.service-class"); // getInitParameter("service-class");
				Class homeClass = loadClass(className);
				_homeImpl = homeClass.newInstance();
				init();
			} else {
//				if (getClass().equals(Hessian.class))
//					throw new ServletException(
//							"server must extend HessianServlet");
				throw new Exception("Fail to initial homeImpl of Hessian");
				//_homeImpl = this;
			}

			if (_homeAPI != null) {
			} else if (Play.configuration.getProperty("hessian.home-api") != null) {
				String className = Play.configuration
						.getProperty("hessian.home-api");// getInitParameter("home-api");
				_homeAPI = loadClass(className);
			} else if (Play.configuration.getProperty("hessian.api-class") != null) {
				String className = Play.configuration
						.getProperty("hessian.api-class"); // getInitParameter("api-class");
				_homeAPI = loadClass(className);
			} else if (_homeImpl != null) {
				_homeAPI = findRemoteAPI(_homeImpl.getClass());
				if (_homeAPI == null)
					_homeAPI = _homeImpl.getClass();
			}

			if (_objectImpl != null) {
			} else if (Play.configuration.getProperty("hessian.object-class") != null) {
				String className = Play.configuration
						.getProperty("hessian.object-class");
				Class objectClass = loadClass(className);
				_objectImpl = objectClass.newInstance();
				init();
			}

			if (_objectAPI != null) {
			} else if (Play.configuration.getProperty("hessian.object-api") != null) {
				String className = Play.configuration
						.getProperty("hessian.object-api");
				_objectAPI = loadClass(className);
			} else if (_objectImpl != null)
				_objectAPI = _objectImpl.getClass();

			_homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);
			
			if (_objectAPI != null)
				_homeSkeleton.setObjectClass(_objectAPI);

			if (_objectImpl != null) {
				_objectSkeleton = new HessianSkeleton(_objectImpl, _objectAPI);
				_objectSkeleton.setHomeClass(_homeAPI);
			} else
				_objectSkeleton = _homeSkeleton;

			if ("true".equals(Play.configuration.getProperty("hessian.debug")))
				isDebug = true;

			if ("false".equals(Play.configuration
					.getProperty("hessian.send-collection-type")))
				setSendCollectionType(false);
		} catch (Exception e) {
			throw new Exception(e);
		}
	}

	private static Class findRemoteAPI(Class implClass) {
		if (implClass == null || implClass.equals(GenericService.class))
			return null;

		Class[] interfaces = implClass.getInterfaces();

		if (interfaces.length == 1)
			return interfaces[0];

		return findRemoteAPI(implClass.getSuperclass());
	}

	private static Class loadClass(String className) throws ClassNotFoundException {
		ClassLoader loader = getContextClassLoader();

		if (loader != null)
			return Class.forName(className, false, loader);
		else
			return Class.forName(className);
	}

	protected static ClassLoader getContextClassLoader() {
		return Thread.currentThread().getContextClassLoader();
	}


	/**
	 * Execute a request. The path-info of the request selects the bean. Once
	 * the bean's selected, it will be applied.
	 */
	public static void service() throws IOException, Exception {
		if(_homeSkeleton == null){
			init();
		}
		/*
		if (!request.method.equals("POST")) {
			response.status = 500;// .setStatus(500, "Hessian Requires POST");
			PrintWriter out = new PrintWriter(response.out);// getWriter();

			response.contentType = "text/html";
			out.println("<h1>Hessian Requires POST</h1>");

			return;
		}
		*/
		// String serviceId = request.path; //getPathInfo();
		String objectId = request.params.get("id"); // getParameter("id");
		if (objectId == null)
			objectId = request.params.get("ejbid"); // getParameter("ejbid");

		// ServiceContext.begin(request, serviceId, objectId);

		try {
			InputStream is = request.body; // getInputStream();
			OutputStream os = response.out; // getOutputStream();

			response.contentType = "application/x-hessian";

			SerializerFactory serializerFactory = getSerializerFactory();

			invoke(is, os, objectId, serializerFactory);
		} catch (RuntimeException e) {
			throw e;
		} catch (Throwable e) {
			throw new Exception(e);
		} finally {
			// ServiceContext.end();
		}
	}

	protected static void invoke(InputStream is, OutputStream os,
			String objectId, SerializerFactory serializerFactory)
			throws Exception {
		if (objectId != null)
			_objectSkeleton.invoke(is, os, serializerFactory);
		else
			_homeSkeleton.invoke(is, os, serializerFactory);
	}

	protected Hessian2Input createHessian2Input(InputStream is) {
		return new Hessian2Input(is);
	}

	/*
	static class LogWriter extends Writer {
		private Logger _log;
		private StringBuilder _sb = new StringBuilder();

		LogWriter(Logger log) {
			_log = log;
		}

		public void write(char ch) {
			if (ch == '\n' && _sb.length() > 0) {
				_log.fine(_sb.toString());
				_sb.setLength(0);
			} else
				_sb.append((char) ch);
		}

		public void write(char[] buffer, int offset, int length) {
			for (int i = 0; i < length; i++) {
				char ch = buffer[offset + i];

				if (ch == '\n' && _sb.length() > 0) {
					_log.fine(_sb.toString());
					_sb.setLength(0);
				} else
					_sb.append((char) ch);
			}
		}

		public void flush() {
		}

		public void close() {
		}
	}
	*/
}
